package geom;


//import PolarVector;

import processing.core.*;
public class Vector implements PConstants{
	public static float EPSILON = 0.06f;
	public float x, y, z;
	
	public int meta;
	public static int ORIGINAL = 0;
	public static int INTERPOLATED = 1;
	
	public Vector(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	public Vector(float x, float y) {
		this.x = x;
		this.y = y;
		this.z = 0;
	}
	public Vector(Vector v){
		set(v);
	}
	public Vector(float[] f){
		set(f);
	}
	public Vector() {
		this.x = 0;
		this.y = 0;
		this.z = 0;
	}
	
	public void setXYZ(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	public void setXYZ(float x, float y) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	public void set(Vector v){
		x = v.x;
		y = v.y;
		z = v.z;
	}
	public void set(float[] v){
		x = v[0];
		y = v[1];
		z = v[2];
	}
	public void set(float x, float y, float z){
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public void setX(float x) {  this.x = x; }
	public void setY(float y) {  this.y = y; }
	public void setZ(float z) {  this.z = z; }
	
	public float getX() { return x; }
	public float getY() { return y; }
	public float getZ() { return z; }
	
	public PolarVector getPolar(){
		PolarVector pv = new PolarVector();
		float _x = x;
		if(_x == 0) _x = 0.001f;
		float x2 = x * x;
		float y2 = y * y;
		pv.a = (float)Math.atan(y/_x);
		if(_x < 0) pv.a += (float)Math.PI;
		pv.e = -(float)Math.atan(z / Math.sqrt(x2 + y2));
		pv.d = (float)Math.sqrt(x2 + y2 + z*z);
		if(pv.a == Float.NaN || pv.e == Float.NaN){
			PApplet.println("NAN");
		}
		return pv;
	}
	
	public float[] getArray(){ return new float[]{x, y, z}; }
	
	public Vector add(Vector v){ return new Vector(x+v.x, y+v.y, z+v.z); }
	public void addMe(Vector v){ set(add(v));}
	
	public Vector sub(Vector v){ return new Vector(x-v.x, y-v.y, z-v.z); }
	public void subMe(Vector v){ set(sub(v)); }
	
	public Vector mult(float p){ return new Vector(p*x, p*y, p*z); }	
	public void multMe(float p){ set(mult(p)); }
	public Vector mult(Vector v){
		return new Vector(x * v.x, y * v.y, z * v.z);
	}
	public Vector div(float p){  return new Vector(x/p, y/p, z/p); }
	public void divMe(float p){  set(div(p)); }
	
	public Vector div(Vector v){
		return new Vector(x / v.x, y / v.y, z / v.z);
	}
	
	public Vector xProdut(Vector v){
		Vector u = this;
		float x = u.y * v.z - u.z * v.y;
		float y = u.z * v.x - u.x * v.z;
		float z = u.x * v.y - u.y * v.x;
		return new Vector(x, y, z);
	}
	
	public float dotProduct(Vector v){
		return v.x * x + v.y * y + v.z * z;
	}
	public Vector scaleLengthTo(float l){
		return div(PApplet.sqrt(x*x + y*y + z*z) / l);
	}
	public Vector normalize(){   return div(PApplet.sqrt(x*x + y*y + z*z)); }
	public void normalizeMe(){   set(normalize()); }
	
	public Vector applyMatrix(PMatrix pm){
		float[] r = new float[3];
		pm.mult3(getArray(), r);
		return new Vector(r);
	}
	public void applyMatrixMe(PMatrix pm){ set(applyMatrix(pm)); }
	
	public static float angleDifference(Vector v1,  Vector v2, Vector v3){
		PolarVector pv1 = v2.sub(v1).getPolar();
		PolarVector pv2 = v3.sub(v2).getPolar();
		float da = PApplet.abs(pv2.a - pv1.a);
		float de = PApplet.abs(pv2.e - pv1.e);
		return da + de;
	}
	
	public void rotate(float a, Vector c){
		Vector cC = new Vector(x,y);
		a = a + Geom.angleXY(c,cC);
		float r = Geom.distance(c,cC);
		set(Geom.getXY(a, r));
		//System.out.println(c + "  a  " + a + "  r  " + r); 
	}
	//scales this point by a value k, in relation to a second point
	public void scale(float k, Vector c){ setXYZ((x - c.x) * k + c.x, (y - c.y) * k + c.y); }
	public void scale(float k,float l, Vector c){ setXYZ((x - c.x) * k + c.x, (y - c.y) * l + c.y); }
	public void move(Vector p){ x = x + p.x; y = y + p.y; }
	public void move(float X, float Y) { move(new Vector(X,Y)); }
	
	public boolean equalTo(Vector v){
		return (x == v.x && y == v.y && z == v.z);
	}
	
	public float absLength(){
		return Geom.distance(0, 0, 0, x, y, z);
	}
	
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		sb.append(x);
		sb.append(", ");
		sb.append(y);
		sb.append(", ");
		sb.append(z);
		sb.append("}");
		return sb.toString();
	}
	
	public Vector getCopy(){
		return new Vector(x, y, z);
	}
	
	public float getDistance(Vector v){
		return Geom.distance(this, v);
	}
	public float getSqrDistance(Vector v){
		return Geom.sqrDistance(this, v);
	}
	
	public boolean isAlmostEqual(Vector v){
		return getDistance(v) < EPSILON;
	}
	
	public boolean isAlmostEqual(Vector v, float threshold){
		return getDistance(v) < threshold;
	}
	
	public Vector getCenter(Vector v){
		return this.add(v.sub(this).mult(0.5f));
	}
	
	public Vector mapToSpace(PMatrix pm){
		Vector v = getCopy();
		PMatrix pmi = new PMatrix();
		pmi.invApply(pm);
		float[] vf = v.getArray();
		pmi.mult3(vf, vf);
		return new Vector(vf);
	}
	public int getMeta() {
		return meta;
	}
	public void setMeta(int meta) {
		this.meta = meta;
		
	}
	
	public float manhattanDistance(Vector v){
		return Math.abs(v.x-x) + Math.abs(v.y-y) + Math.abs(v.z-z);
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + meta;
		result = prime * result + Float.floatToIntBits(x);
		result = prime * result + Float.floatToIntBits(y);
		result = prime * result + Float.floatToIntBits(z);
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Vector))
			return false;
		final Vector other = (Vector) obj;
		if (meta != other.meta)
			return false;
		if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
			return false;
		if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
			return false;
		if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z))
			return false;
		return true;
	}
	
	
	
}
